#include "rtp/FeedbackPsRemb.h"
#include "utils.h"

namespace Rtp {
    namespace Rtcp {
        FeedbackPsRembPacket* FeedbackPsRembPacket::Parse(const uint8_t *data, size_t len) {
            if (len < Packet::CommonHeaderSize + FeedbackPacket::HeaderSize + 8u) {
                return nullptr;
            }

            auto* commonHeader = const_cast<CommonHeader *>(reinterpret_cast<const CommonHeader *>(data));
            std::unique_ptr<FeedbackPsRembPacket> packet(new FeedbackPsRembPacket(commonHeader, len));
            if (!packet->IsCorrect()) {
                return nullptr;
            }

            return packet.release();
        }

        FeedbackPsRembPacket::FeedbackPsRembPacket(CommonHeader *commonHeader, size_t availableLen):FeedbackPsAfbPacket(commonHeader, FeedbackPsAfbPacket::Application::REMB) {
            size_t len = static_cast<size_t>(ntohs(commonHeader->length) + 1) *4;
            if (len > availableLen) {
                this->isCorrect = false;
                return;
            }

            auto* data = reinterpret_cast<uint8_t*>(commonHeader) + Packet::CommonHeaderSize + FeedbackPacket::HeaderSize;
            size_t numSsrcs = data[4];

            if (len != Packet::CommonHeaderSize + FeedbackPacket::HeaderSize + 8u + (numSsrc * 4u)) {
                this->isCorrect = false;
                return;
            }

            if (Utils::Byte::Get4Bytes(data, 0) != FeedbackPsRembPacket::UniqueIdentifier) {
                this->isCorrect = false;
                return;
            }

            uint8_t exponent = data[5] >> 2;
            uint64_t mantissa = (static_cast<uint32_t>(data[5] & 0x03) << 16) | Utils::Byte::Get2Bytes(data, 6);
            this->bitrate = (mantissa << exponent);

            if ((this->bitrate >> exponent) != mantissa) {
                this->isCorrect = false;
                return;
            }

            size_t index { 8 };
            this->ssrcs.reserve(numSsrcs);
            for (size_t n { 0 }; n < numSsrcs; ++n ) {
                this->ssrcs.push_back(Utils::Byte::Get4Bytes(data, index));
                index += 4u;
            }
        }

        size_t FeedbackPsRembPacket::Serialize(uint8_t* buffer) {
            size_t offset = FeedbackPsPacket::Serialize(buffer);
            uint64_t mantissa = this->bitrate;
            uint8_t exponent { 0u };
            while (mantissa > 0x3FFFF)
            {
                mantissa >>= 1;
                ++exponent;
            }
            
            Utils::Byte::Set4Bytes(buffer, offset, FeedbackPsRembPacket::UniqueIdentifier);
            offset += FeedbackPsRembPacket::UniqueIdentifierSize;

            buffer[offset] = this->ssrcs.size();
            offset += 1;

            Utils::Byte::Set2Bytes(buffer, offset, mantissa & 0xFFFF);
            offset += 2;

            for (auto ssrc : this->ssrcs) {
                Utils::Byte::Set4Bytes(buffer, offset, ssrc);
                offset += 4u;
            }

            return offset;
        }

        void FeedbackPsRembPacket::Dump() const {
            FeedbackPsPacket::Dump();
        }
    }
}